//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>
#include <regs.h>
#include <_hal.h>
#include <mantle.h>
#include <exception.h>

EXTERN_C int        s_nIndex = 0;

EXTERN_C void FillTlb(Address vAddr, InterruptContext *pContext)
{
    Address         lo0, lo1;
    PageDirEntry    *pageDEntry;
    PageTabEntry    *pageTEntry;
    uint32_t        uHighOrLow;
    CProcess        *pCurrentProcess;
    ECode           ec;
    Address         faultAddress;
    uint32_t        uASID;

    faultAddress = vAddr;
    uHighOrLow = (vAddr >> 12) & 1;
    vAddr &= 0xffffe000;

    InvalidTLB(vAddr);

    pageDEntry = (PageDirEntry *)(((uint32_t *)HAT::GetCurrent())[0]);
    assert(0 != pageDEntry);

    pageTEntry = (PageTabEntry *)(pageDEntry[PAGEDIR_INDEX(vAddr)].uValue);

    if (NULL == pageTEntry) {
        goto PageFaultException;
    }

    lo0 = pageTEntry[(PAGETAB_INDEX(vAddr) & 0x3fe) + 0].uValue;
    lo1 = pageTEntry[(PAGETAB_INDEX(vAddr) & 0x3fe) + 1].uValue;

    switch (uHighOrLow) {
        case 0:
            if (NULL == lo0) {
                goto PageFaultException;
            }

            break;
        case 1:
            if (NULL == lo1) {
                goto PageFaultException;
            }

            break;
        default:
            assert(0 && "It's impossible");
    }

#if defined(_GNUC)
    ASM("mfc0   %0, $10;":"=r&"(uASID));
#elif defined(_EVC)
    ASM(
        "mfc0   t0, $10;"
        "sw     t0, 0(%0);", &uASID);
#else
#error unknown compiler
#endif
    uASID &= 0xff;
    vAddr = vAddr | uASID;

    FillRandomEntry(vAddr, lo0, lo1);

    return;

PageFaultException:

#if 0
    kprintf("PageFault, address = 0x%08x\n", faultAddress);
    DumpCxt(pContext);
    Cli();
    myChat((Address)pContext);
#endif

    AcknowledgeException();
    pCurrentProcess = GetCurrentProcess();

    if (pCurrentProcess != NULL) {
        ec = pCurrentProcess->PageFault(
            (uint_t)faultAddress, pContext->regs[SP],
            IS_IN_KERNEL_MODE(pContext), FALSE);
        if (SUCCEEDED(ec)) {
            return;
        }
#ifdef _DEBUG
else {
    kprintf("PageFault 0x%08x failed, the ec = 0x%08x\n", faultAddress, ec);
}
#endif
    }

    Cli();
    ViewCurrentTlb();
    DumpCxt(pContext);
    myChat(pContext);
}

EXTERN_C void InvalidTLB(Address vAddr)
{
    int     nIndex;

    nIndex = FindTlb(vAddr);
    if (nIndex > 0) {
        assert(nIndex < 48 && nIndex >= s_nIndex);
        InvalidEntry(nIndex);
    }
}

EXTERN_C void FillGlobalTlb(Address vAddr)
{
    Address         lo0, lo1;
    PageDirEntry    *pageDEntry;

    pageDEntry = (PageDirEntry *)(((uint32_t *)HAT::GetCurrent())[0]);

    assert(0 != pageDEntry);

    lo0 = ((PageTabEntry *)(pageDEntry[PAGEDIR_INDEX(vAddr)].uValue))
                                    [(PAGETAB_INDEX(vAddr) & 0x3fe) + 0].uValue;
    lo1 = ((PageTabEntry *)(pageDEntry[PAGEDIR_INDEX(vAddr)].uValue))
                                    [(PAGETAB_INDEX(vAddr) & 0x3fe) + 1].uValue;
    lo0 |= 1;   // It's Global
    lo1 |= 1;   // It's Global

    InvalidTLB(vAddr);

    FillAssignEntry(s_nIndex, vAddr, lo0, lo1);

    s_nIndex++;

    write_cp0_register($6, s_nIndex);

    ASM(
        ".set   noat;"
        "li     a3, 47;"
        "mtc0   a3, $1;"
        ".set   at");
}

EXTERN_C void ViewCurrentTlb()
{
    uint32_t    uEntryHi, uEntryLo0, uEntryLo1, uPageMask;

    uint32_t    uWired;
    read_cp0_register($10, uEntryHi);
    uEntryHi &= 0xff;
    kprintf("The current ASID = 0x%08x\n", uEntryHi);
    read_cp0_register($6, uWired);
    kprintf("The wired register = 0x%08x\n", uWired);

    kprintf("Index\tEntryHi\t\tEntryLo0\tEntryLo1\n");
    for (int i = 0; i < MAX_TLB_ENTRY; i++) {
        ReadTlb(i, &uEntryHi, &uEntryLo0, &uEntryLo1, &uPageMask);
        kprintf("%d\t0x%08x\t0x%08x\t0x%08x\n",
                    i, uEntryHi, uEntryLo0, uEntryLo1);
    }
}
